let WxRenderer = function(opts) {
    this.opts = opts;
    let ENV_USE_REFERENCES = true;
    let ENV_STRETCH_IMAGE = true;

    let footnotes = [];
    let footnoteIndex = 0;
    let styleMapping = null;

    let CODE_FONT_FAMILY = "Menlo, Operator Mono, Consolas, Monaco, monospace";

    let merge = function(base, extend) {
        return Object.assign({}, base, extend)
    };

    this.buildTheme = function(themeTpl) {
        let mapping = {};
        let base = merge(themeTpl.BASE, {
            'font-family': this.opts.fonts,
            'font-size': this.opts.size
        });
        let base_block = merge(base, {});
        for (let ele in themeTpl.inline) {
            if (themeTpl.inline.hasOwnProperty(ele)) {
                let style = themeTpl.inline[ele];
                if (ele === 'codespan') {
                    style['font-family'] = CODE_FONT_FAMILY;
                    style['white-space'] = 'normal';
                }
                mapping[ele] = merge(base, style)
            }
        }
        for (let ele in themeTpl.block) {
            if (themeTpl.block.hasOwnProperty(ele)) {
                let style = themeTpl.block[ele];
                if (ele === 'code') {
                    style['font-family'] = CODE_FONT_FAMILY
                }
                mapping[ele] = merge(base_block, style)
            }
        }
        return mapping
    };

    let getStyles = function(tokenName, addition) {
        let arr = [];
        let dict = styleMapping[tokenName];
        if (!dict) return '';
        for (const key in dict) {
            arr.push(key + ':' + dict[key])
        }
        return `style="${ arr.join(';') + (addition || '') }"`
    };

    let addFootnote = function(title, link) {
        footnoteIndex += 1;
        footnotes.push([footnoteIndex, title, link]);
        return footnoteIndex
    };

    this.buildFootnotes = function() {
        let footnoteArray = footnotes.map(function(x) {
            if (x[1] === x[2]) {
                return `<code style="font-size: 90%; opacity: 0.6;">[${ x[0] }]</code>: <i>${ x[1] }</i><br/>`
            }
            return `<code style="font-size: 90%; opacity: 0.6;">[${ x[0] }]</code> ${ x[1] }: <i>${ x[2] }</i><br/>`
        });
        return `<h4><span class="prefix"></span> <span class="content" ${ getStyles('h4') }>注解</span><span class="suffix"></span></h4><p ${ getStyles('footnotes') }>${ footnoteArray.join('\n') }</p>`
    };

    this.buildAddition = function() {
        return '<style>.preview-wrapper pre::before{' +
            'font-family:"SourceSansPro","HelveticaNeue",Arial,sans-serif;' +
            // 'position:absolute;' +
            'top:0;' +
            'right:0;' +
            'color:#ccc;' +
            'text-align:right;' +
            'font-size:0.8em;' +
            'padding:5px10px0;' +
            'line-height:15px;' +
            'height:15px;' +
            'font-weight:600;' +
            '}</style>'
    };

    this.setOptions = function(newOpts) {
        this.opts = merge(this.opts, newOpts)
    };

    this.hasFootnotes = function() {
        return footnotes.length !== 0
    };

    this.getRenderer = function() {
        footnotes = [];
        footnoteIndex = 0;

        styleMapping = this.buildTheme(this.opts.theme);
        let renderer = new marked.Renderer();
        FuriganaMD.register(renderer);

        renderer.heading = function(text, level) {
            switch (level) {
                case 1:
                    return `<h1 ${ getStyles('h1') }>${ text }</h1>`;
                case 2:
                    // return `<h2> ${ getStyles('h2') }>${ text }</h2>`;
                    return `<h2><span class="prefix"></span> <span class="content" ${ getStyles('h2') }>${ text }</span><span class="suffix"></span></h2>`;
                case 3:
                    // return `<h3 ${ getStyles('h3') }>${ text }</h3>`;
                    return `<h3><span class="prefix"></span> <span class="content" ${ getStyles('h3') }>${ text }</span><span class="suffix"></span></h3>`;
                default:
                    // return `<h4 ${ getStyles('h4') }>${ text }</h4>`;
                    return `<h4><span class="prefix"></span> <span class="content" ${ getStyles('h4') }>${ text }</span><span class="suffix"></span></h4>`;
            }
        };
        renderer.paragraph = function(text) {
            // return `<p ${ getStyles('p') }>${ text }</p>`
            return `<p>${ text }</p>`
        };
        renderer.blockquote = function(text) {
            // text = text.replace(/<p.*?>/, `<p ${ getStyles('blockquote_p') }>`);
            text = text.replace(/<p.*?>/, `<p>`);
            return `<blockquote>${ text }</blockquote>`
                // return `<blockquote ${ getStyles('blockquote') }><p>${ text }</p></blockquote>`
        };
        renderer.code = function(text, infoString) {
            text = text.replace(/</g, "&lt;");
            text = text.replace(/>/g, "&gt;");

            let lines = text.split('\n');
            let codeLines = [];
            let numbers = [];
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i];
                // codeLines.push(`<code class="prettyprint"><span class="code-snippet_outer">${ (line || '<br>') }</span></code>`);
                codeLines.push(`${ (line || '<br>') }`);
                // numbers.push('<li></li>')
            }
            let lang = infoString || '';
            // return `<section class="code-snippet__fix code-snippet__js">` +
            //     `<ul class="code-snippet__line-index code-snippet__js">${ numbers.join('') }</ul>` +
            //     `<pre class="code-snippet__js" data-lang="${ lang }">` +
            //     codeLines.join('') +
            //     `</pre></section>`
            return `<pre class="custom" data-lang="${ lang }"><code class="hljs">` +
                codeLines.join('\n') + `</code></pre>`
        };
        renderer.codespan = function(text, infoString) {
            return `<code ${ getStyles('codespan') }>${ text }</code>`
        };
        renderer.listitem = function(text) {
            return `<span ${ getStyles('listitem') }><span style="margin-right: 10px;"><%s/></span>${ text }</span>`;
        };
        renderer.list = function(text, ordered, start) {
            text = text.replace(/<\/*p.*?>/g, '');
            let segments = text.split(`<%s/>`);
            if (!ordered) {
                text = segments.join('•');
                return `<p ${ getStyles('ul') }>${ text }</p>`;
            }
            text = segments[0];
            for (let i = 1; i < segments.length; i++) {
                text = text + i + '.' + segments[i];
            }
            return `<p ${ getStyles('ol') }>${ text }</p>`;
        };
        renderer.image = function(href, title, text) {
            let subText = '';
            if (text) {
                subText = `<figcaption ${ getStyles('figcaption') }>${ text }</figcaption>`
            }
            let figureStyles = getStyles('figure');
            let imgStyles = getStyles(ENV_STRETCH_IMAGE ? 'image' : 'image_org');
            return `<figure ${ figureStyles }><img ${ imgStyles } src="${ href }" title="${ title }" alt="${ text }"/>${ subText }</figure>`
        };
        renderer.link = function(href, title, text) {
            if (href.indexOf('http://mp.weixin.qq.com') === 0) {
                return `<a href="${ href }" title="${ (title || text) }" ${ getStyles('wx_link') }>${ text }</a>`;
            } else if (href === text) {
                return text;
            } else {
                if (ENV_USE_REFERENCES) {
                    let ref = addFootnote(title || text, href);
                    return `<span ${ getStyles('link') }>${ text }<sup>[${ ref }]</sup></span>`;
                } else {
                    return `<a href="${ href }" title="${ (title || text) }" ${ getStyles('link') }>${ text }</a>`;
                }
            }
        };
        renderer.strong = function(text) {
            return `<strong ${ getStyles('strong') }>${ text }</strong>`;
        };
        renderer.em = function(text) {
            // return `<p ${ getStyles('p', ';font-style: italic;')}>${ text }</p>`
            // return `<p ${ getStyles('p', ';font-style: italic;')}><em>${ text }</em></p>`
            return `<em>${ text }</em>`
        };
        renderer.table = function(header, body) {
            return `<table class="preview-table"><thead ${ getStyles('thead') }>${ header }</thead><tbody>${ body }</tbody></table>`;
        };
        renderer.tablecell = function(text, flags) {
            return `<td ${ getStyles('td') }>${ text }</td>`;
        };
        renderer.hr = function() {
            return `<hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);">`;
        };
        return renderer
    }
};